"""
MinIO对象存储服务类
目的：封装MinIO客户端操作，提供统一的文件上传、下载、管理接口
职责：处理文件存储相关业务，包括图片、文档等文件的上传下载
"""

import os
import io
import uuid
from datetime import timedelta
from typing import Optional, Dict, Any
from minio import Minio
from minio.error import S3Error

from app.utils.response import Result


class MinioService:
    """
    MinIO文件存储服务类
    目的：提供统一的文件存储服务，支持图片、文档等文件的上传下载管理
    职责：封装MinIO操作，处理文件存储逻辑，返回统一格式的结果
    """

    # 允许上传的图片类型
    ALLOWED_IMAGE_TYPES = {
        'image/jpeg': '.jpg',
        'image/jpg': '.jpg',
        'image/png': '.png',
        'image/gif': '.gif',
        'image/webp': '.webp',
        'image/bmp': '.bmp'
    }

    # 允许上传的文档类型
    ALLOWED_DOCUMENT_TYPES = {
        'application/pdf': '.pdf',
        'application/msword': '.doc',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',
        'application/vnd.ms-excel': '.xls',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
        'text/plain': '.txt',
        'text/markdown': '.md'
    }

    # 文件大小限制（10MB）
    MAX_FILE_SIZE = 10 * 1024 * 1024

    def __init__(self):
        """
        初始化MinIO客户端
        目的：创建MinIO连接实例，配置存储参数
        """
        # MinIO服务器配置，直接使用硬编码配置
        self.endpoint = '43.139.187.139:9000'
        self.access_key = 'admin'
        self.secret_key = 'BYVjgofraYp3D2fO'
        self.secure = False

        # 存储桶名称配置
        self.bucket_images = 'blog-images'
        self.bucket_documents = 'blog-documents'
        self.bucket_others = 'blog-others'

        # 打印调试信息
        print(f"MinIO配置 - Endpoint: {self.endpoint}, Access Key: {self.access_key}, Secure: {self.secure}")

        # 初始化MinIO客户端
        self.client = Minio(
            self.endpoint,
            access_key=self.access_key,
            secret_key=self.secret_key,
            secure=self.secure
        )

        # 确保存储桶存在
        self._ensure_buckets()

    def _ensure_buckets(self):
        """
        确保必要的存储桶存在
        目的：检查并创建应用所需的存储桶
        """
        buckets = [self.bucket_images, self.bucket_documents, self.bucket_others]
        for bucket in buckets:
            try:
                print(f"检查存储桶: {bucket}")
                if not self.client.bucket_exists(bucket):
                    print(f"创建存储桶: {bucket}")
                    self.client.make_bucket(bucket)
                    print(f"✓ 存储桶 {bucket} 创建成功")
                else:
                    print(f"✓ 存储桶 {bucket} 已存在")
            except S3Error as e:
                print(f"✗ 创建存储桶失败 {bucket}: {e}")
                print(f"  错误代码: {e.code}")
                print(f"  错误消息: {e.message}")

    def _get_file_extension(self, content_type: str, file_type: str = 'image') -> str:
        """
        根据内容类型获取文件扩展名

        Args:
            content_type: 内容类型，如'image/jpeg'
            file_type: 文件类型（image/document）

        Returns:
            str: 文件扩展名，如'.jpg'
        """
        if file_type == 'image':
            return self.ALLOWED_IMAGE_TYPES.get(content_type, '.bin')
        elif file_type == 'document':
            return self.ALLOWED_DOCUMENT_TYPES.get(content_type, '.bin')
        return '.bin'

    def _generate_object_name(self, prefix: str = 'images', extension: str = '.jpg') -> str:
        """
        生成唯一的对象名称

        Args:
            prefix: 对象前缀，如'images'、'avatars'等
            extension: 文件扩展名

        Returns:
            str: 唯一的对象名称
        """
        unique_id = str(uuid.uuid4()).replace('-', '')
        date_path = os.path.join(
            prefix,
            *str(uuid.uuid4())[:8],
            f"{unique_id}{extension}"
        )
        return date_path

    def upload_image(self, file_data: bytes, content_type: str,
                    object_name: Optional[str] = None,
                    prefix: str = 'images') -> Result:
        """
        上传图片文件

        Args:
            file_data: 文件二进制数据
            content_type: 内容类型
            object_name: 自定义对象名称（可选）
            prefix: 对象前缀

        Returns:
            Result: 上传结果，包含文件URL等信息
        """
        try:
            # 验证文件类型
            if content_type not in self.ALLOWED_IMAGE_TYPES:
                return Result.validation_error(f"不支持的图片类型: {content_type}")

            # 验证文件大小
            if len(file_data) > self.MAX_FILE_SIZE:
                return Result.validation_error(f"文件大小超过限制: {self.MAX_FILE_SIZE / 1024 / 1024}MB")

            # 生成对象名称
            if not object_name:
                extension = self._get_file_extension(content_type, 'image')
                object_name = self._generate_object_name(prefix, extension)

            # 上传文件
            result = self.client.put_object(
                self.bucket_images,
                object_name,
                io.BytesIO(file_data),
                length=len(file_data),
                content_type=content_type
            )

            # 生成访问URL
            url = self.get_presigned_url(object_name, 'images')

            return Result.success(data={
                'object_name': object_name,
                'url': url,
                'size': len(file_data),
                'content_type': content_type
            }, message="图片上传成功")

        except S3Error as e:
            return Result.internal_error(f"上传失败: {str(e)}")
        except Exception as e:
            return Result.internal_error(f"上传异常: {str(e)}")

    def upload_document(self, file_data: bytes, content_type: str,
                       object_name: Optional[str] = None,
                       prefix: str = 'documents') -> Result:
        """
        上传文档文件

        Args:
            file_data: 文件二进制数据
            content_type: 内容类型
            object_name: 自定义对象名称（可选）
            prefix: 对象前缀

        Returns:
            Result: 上传结果，包含文件URL等信息
        """
        try:
            # 验证文件类型
            if content_type not in self.ALLOWED_DOCUMENT_TYPES:
                return Result.validation_error(f"不支持的文档类型: {content_type}")

            # 验证文件大小
            if len(file_data) > self.MAX_FILE_SIZE:
                return Result.validation_error(f"文件大小超过限制: {self.MAX_FILE_SIZE / 1024 / 1024}MB")

            # 生成对象名称
            if not object_name:
                extension = self._get_file_extension(content_type, 'document')
                object_name = self._generate_object_name(prefix, extension)

            # 上传文件
            result = self.client.put_object(
                self.bucket_documents,
                object_name,
                io.BytesIO(file_data),
                length=len(file_data),
                content_type=content_type
            )

            # 生成访问URL
            url = self.get_presigned_url(object_name, 'documents')

            return Result.success(data={
                'object_name': object_name,
                'url': url,
                'size': len(file_data),
                'content_type': content_type
            }, message="文档上传成功")

        except S3Error as e:
            return Result.internal_error(f"上传失败: {str(e)}")
        except Exception as e:
            return Result.internal_error(f"上传异常: {str(e)}")

    def get_presigned_url(self, object_name: str, file_type: str = 'images',
                         expires: int = 3600) -> str:
        """
        获取预签名URL

        Args:
            object_name: 对象名称
            file_type: 文件类型（images/documents/others）
            expires: URL有效期（秒）

        Returns:
            str: 预签名URL
        """
        try:
            # 选择对应的存储桶
            bucket = self.bucket_images
            if file_type == 'documents':
                bucket = self.bucket_documents
            elif file_type == 'others':
                bucket = self.bucket_others

            # 生成预签名URL
            url = self.client.presigned_get_object(
                bucket,
                object_name,
                expires=timedelta(seconds=expires)
            )
            return url

        except S3Error as e:
            raise Exception(f"获取URL失败: {str(e)}")

    def delete_object(self, object_name: str, file_type: str = 'images') -> Result:
        """
        删除对象

        Args:
            object_name: 对象名称
            file_type: 文件类型

        Returns:
            Result: 删除结果
        """
        try:
            # 选择对应的存储桶
            bucket = self.bucket_images
            if file_type == 'documents':
                bucket = self.bucket_documents
            elif file_type == 'others':
                bucket = self.bucket_others

            # 删除对象
            self.client.remove_object(bucket, object_name)

            return Result.success(message="文件删除成功")

        except S3Error as e:
            return Result.internal_error(f"删除失败: {str(e)}")
        except Exception as e:
            return Result.internal_error(f"删除异常: {str(e)}")

    def get_object_info(self, object_name: str, file_type: str = 'images') -> Result:
        """
        获取对象信息

        Args:
            object_name: 对象名称
            file_type: 文件类型

        Returns:
            Result: 对象信息
        """
        try:
            # 选择对应的存储桶
            bucket = self.bucket_images
            if file_type == 'documents':
                bucket = self.bucket_documents
            elif file_type == 'others':
                bucket = self.bucket_others

            # 获取对象状态
            stat = self.client.stat_object(bucket, object_name)

            # 生成访问URL
            url = self.get_presigned_url(object_name, file_type)

            return Result.success(data={
                'object_name': object_name,
                'url': url,
                'size': stat.size,
                'content_type': stat.content_type,
                'last_modified': stat.last_modified,
                'etag': stat.etag
            })

        except S3Error as e:
            return Result.not_found(f"文件不存在: {str(e)}")
        except Exception as e:
            return Result.internal_error(f"获取信息失败: {str(e)}")

    def upload_avatar(self, file_data: bytes, content_type: str, user_id: int) -> Result:
        """
        上传用户头像（专用方法）

        Args:
            file_data: 头像文件数据
            content_type: 内容类型
            user_id: 用户ID

        Returns:
            Result: 上传结果
        """
        try:
            # 验证图片类型
            if content_type not in self.ALLOWED_IMAGE_TYPES:
                return Result.validation_error("头像只支持 JPG、PNG、GIF 格式")

            # 验证文件大小（头像限制2MB）
            avatar_max_size = 2 * 1024 * 1024
            if len(file_data) > avatar_max_size:
                return Result.validation_error("头像文件大小不能超过2MB")

            # 生成对象名称（按用户ID组织）
            extension = self._get_file_extension(content_type, 'image')
            object_name = f"avatars/{user_id}/{uuid.uuid4().hex}{extension}"

            # 上传头像
            result = self.upload_image(file_data, content_type, object_name, 'avatars')

            if result.code == 200:
                # 返回头像URL（7天有效期，MinIO最大限制）
                avatar_url = self.get_presigned_url(object_name, 'images', expires=86400*7)
                result.data['avatar_url'] = avatar_url
                result.data['thumbnail_url'] = avatar_url  # 后续可添加缩略图支持

            return result

        except Exception as e:
            return Result.internal_error(f"上传头像失败: {str(e)}")

    def upload_checkin_image(self, file_data: bytes, content_type: str,
                           user_id: int, checkin_id: int) -> Result:
        """
        上传打卡图片（专用方法）

        Args:
            file_data: 图片文件数据
            content_type: 内容类型
            user_id: 用户ID
            checkin_id: 打卡记录ID

        Returns:
            Result: 上传结果
        """
        try:
            # 验证图片类型
            if content_type not in self.ALLOWED_IMAGE_TYPES:
                return Result.validation_error("打卡图片只支持 JPG、PNG、GIF 格式")

            # 生成对象名称（按用户和打卡记录组织）
            extension = self._get_file_extension(content_type, 'image')
            object_name = f"checkins/{user_id}/{checkin_id}/{uuid.uuid4().hex}{extension}"

            # 上传图片
            result = self.upload_image(file_data, content_type, object_name, 'checkins')

            if result.code == 200:
                # 返回图片URL（7天有效期，MinIO最大限制）
                image_url = self.get_presigned_url(object_name, 'images', expires=86400*7)
                result.data['image_url'] = image_url

            return result

        except Exception as e:
            return Result.internal_error(f"上传打卡图片失败: {str(e)}")